提升(Hosting)是 JavaScript 中的一種行為,指的是 在宣告函式或變數之前如果先使用它,也不會出錯。
console.log(number); // 得到 undefined 而不是出錯
var number;
不過這邊要記得:只有宣告會提升、賦值不會。
也就是說如果改成這樣:
console.log(number); // 一樣是 undefined
var number = 1;
let
與 const
同樣也有提升的概念,與 var
的差別在於 var
提升後會被初始化為 undefined
,這就是為什麼下面的程式碼會出現此錯誤。
console.log(number);
// 輸入後出現
app.js:1 Uncaught ReferenceError: Cannot access 'number' before initialization
在「提升之後」以及「賦值之前」這段期間內只要試圖存取就會噴錯,這段期間稱為 暫時死區(Temporal Dead Zone,TDZ)
。
函式與宣告一樣具有提升效果,且優先權會大於使用 var
宣告。下面的例子是兩個名稱衝突,function 會提升至 var
宣告:
console.log(a); // 印出 function
var a;
function a(){};
以上是 函式陳述式 (前面是 function)的例子,如果用 函式表達式 的話同樣會拿到 undefined
。
除此之外,因為 function scope
的原因,在裡面宣告的變數不會提升到外面去。
提升看起來是單純地將變數和函式宣告,移動到程式的區塊頂端,然而並非如此。變數和函數的宣告會在編譯階段就被放入記憶體,但實際位置和程式碼中完全一樣。
這邊主要參考文章「我知道你懂 hoisting,可是你了解到多深?」中的「hoisting 到底是怎麼運作的?」段落,下文幾乎都是複製貼上,只是節錄幾個自己覺得較為重要的片段(實際上都很重要),建議點進文章看完整篇!
在進入 function 時,會產生一個 Execution Contexts(以下簡稱 EC),裡面儲存跟這個 function 有關的一些資訊,並且把這個 EC 放到 stack 裡面,當 function 執行完以後,就會把 EC 給 pop 出來。
每個 EC 都會有相對應的 variable object(以下簡稱 VO),在裡面宣告的變數跟函式都會被加進 VO 裡面,如果是 function,那參數也會被加到 VO 裡。
進入 EC 時,會依照順序將東西放至 VO:
// EC
function test(a, b, c) {}
test(10)
// VO
{
a: 10,
b: undefined,
c: undefined
}
除此之外,「如果 VO 裡面已經有同名的屬性,就把它覆蓋掉」。對於變數,在 VO 裡面新增一個屬性並且把值設為 undefined,再來是重點:「如果 VO 已經有這個屬性的話,值不會被改變」。
整個的流程: